Java 读书笔记 14.1-14.6 RTTI

RTTI

Runtime Type Information
好吧,时隔多日我又来啃这块骨头了;

RTTI顾名思义 运行时类型信息;编译的时候不知道,只有当具体运行时才知道是什么类型信息;Java编程思想好像也没说是怎么具体实现的;我也只能管中窥豹了;

首先,我们知道多态,那么多态是啥呢?
维基百科说:
多态也可定义为“一种将不同的特殊行为和单个泛化记号相关联的能力”

这就让我想起了继承关系;不论是哪个子类都可以用父类表示;编译时是父类,运行时就变成子类了;这就是RTTI了;厉害吧;

Class对象

首先想知道Class对象是什么,必须清楚类,类实例化之后形成一个对象;
这是我们常见的:类和对象?那么类是什么?对于虚拟机来说,他怎么区别这个对象和那个对象不一样呢? 这就靠类对象了;通过类对象,我们就知道“类型”;
举个例子;我们说狮子,一般是一个名词,指代一种生物,某个狮子就是狮子这个类的实例化;但是我们的大脑,社会上的文字,是要储存狮子的定义的;这样你知道Class对象是什么了吧;

class调用顺序

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Candy {
public Candy() {
System.out.println("ccandy");
}
{
System.out.println("no static");
}
static
{
System.out.println("loading Cnady");
}
}

1
2
3
4
5
6
7
8
9
10
11
public class SweetShop {
public static void main(String [] arg) throws ClassNotFoundException {
new Candy();
Class a =Class.forName("Class.Gum"); //会直接加载类
}
}
output:
loading Cnady
no static
ccandy
loading Gun

类字面常量

想调用类有三种方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class ReflectDemo {
public static void main(String[] args) throws ClassNotFoundException {
//第一种:Class c1 = Code.class;
Class class1=ReflectDemo.class;
System.out.println(class1.getName());
//第二种:Class c2 = code1.getClass();
ReflectDemo demo2= new ReflectDemo();
Class c2 = demo2.getClass();
System.out.println(c2.getName());
//第三种:Class c3 = Class.forName("com.trigl.reflect.Code");
Class class3 = Class.forName("com.tengj.reflect.ReflectDemo");
System.out.println(class3.getName());
}
}

第一种的Code.class又叫做类字面常量,好处是不会直接初始化class对象,仅仅是加载;
使用类的准备工作有

  1. 加载
  2. 链接
  3. 初始化,由于java很懒,所以都是啥时候用到啥时候加载,所以直接的forName就有点不好玩了;初始化要延迟到静态方法被调用,(所以构造函数也是静态方法),或者非常数讲台域被引用的时候;

泛化的class引用

  1. 简单地说就是class引用带了类型,只接受这种,或者一部分的class对象;
  2. instance与instanceof,简单说一个要比名字,另一个比的是class对象,对象可以动态传参,所以就方便许多;
  3. instance和class比较时的区别 instance会考虑继承;即:麻雀是鸟,但是class不会这么比,麻雀!=鸟;

反射

其实就是通过class对象来获取类中的相关内容比如获取一个方法
以下代码来源:”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public class Person {
private String name;
private int age;
private String msg="hello wrold";
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Person() {
}
private Person(String name) {
this.name = name;
System.out.println(name);
}
public void fun() {
System.out.println("fun");
}
public void fun(String name,int age) {
System.out.println("我叫"+name+",今年"+age+"岁");
}
}
public class ReflectDemo {
public static void main(String[] args){
try {
Class c = Class.forName("com.tengj.reflect.Person");
Object o = c.newInstance();
Method method = c.getMethod("fun", String.class, int.class);
method.invoke(o, "tengj", 10);
} catch (Exception e) {
e.printStackTrace();
}
}
}

或者获取方法组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class ReflectDemo {
public static void main(String[] args){
try {
Class c = Class.forName("com.tengj.reflect.Person");
Method[] methods = c.getDeclaredMethods();
for(Method m:methods){
String methodName= m.getName();
System.out.println(methodName);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class ReflectDemo {
public static void main(String[] args){
try {
Class c = Class.forName("com.tengj.reflect.Person");
//获取成员变量
Field field = c.getDeclaredField("msg"); //因为msg变量是private的,所以不能用getField方法
Object o = c.newInstance();
field.setAccessible(true);//设置是否允许访问,因为该变量是private的,所以要手动设置允许访问,如果msg是public的就不需要这行了。
Object msg = field.get(o);
System.out.println(msg);
} catch (Exception e) {
e.printStackTrace();
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
public class ReflectDemo {
public static void main(String[] args){
try {
Class c = Class.forName("com.tengj.reflect.Person");
//获取构造函数
Constructor constructor = c.getDeclaredConstructor(String.class);
constructor.setAccessible(true);//设置是否允许访问,因为该构造器是private的,所以要手动设置允许访问,如果构造器是public的就不需要这行了。
constructor.newInstance("tengj");
} catch (Exception e) {
e.printStackTrace();
}
}
}

ps:无关思想的细节就不要深究了;

接口与类型信息

1.通过使用反射,即使实现包的访问权限,仍旧可以到达并调用所有方法,甚至是private方法。如果知道方法名,就可以在其Method对象上调用setAccessible(true),然后通过invoke()方法来使用该方法。
2.final域实际上在遭遇修改时是安全的。运行时系统会在不抛异常的情况下接受任何修改尝试,但实际上不会发生任何修改